package graphics.system;
import java.nio.ByteBuffer;

import org.lwjgl.opengl.EXTPackedDepthStencil;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.EXTFramebufferObject;

import tools.Log;
import tools.Point;
public class Framebuffer {
	public int xres, yres;
	private int texid;
	private int dstexid;
	private int fbufferid;
	private boolean stencil;
	private boolean depth;
	private int rbufferid;
	private int depthbufferid;
	public Framebuffer(Point res, boolean stencil, boolean depth) {
		this.init(res, stencil, depth);
		this.stencil = stencil;
		this.depth = depth;
	}
	public void init(Point res, boolean stencil, boolean depth) {
		// boolean FBOEnabled =
		// GLContext.getCapabilities().GL_EXT_framebuffer_object;
		xres = (int) res.x;
		yres = (int) res.y;
		texid = GL11.glGenTextures();
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, texid);
		GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
		GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
		GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
		GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
		GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, xres, yres, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, (ByteBuffer) null);
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
		if (depth || stencil) {
			dstexid = GL11.glGenTextures();
			GL11.glBindTexture(GL11.GL_TEXTURE_2D, dstexid);
			GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
			GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
			GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
			GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
			GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_DEPTH_COMPONENT, xres, yres, 0, GL11.GL_DEPTH_COMPONENT, GL11.GL_UNSIGNED_INT, (ByteBuffer) null);
			GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
		}
		fbufferid = EXTFramebufferObject.glGenFramebuffersEXT();
		EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, fbufferid);
		EXTFramebufferObject.glFramebufferTexture2DEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT, GL11.GL_TEXTURE_2D, texid, 0);
		if (stencil || depth) {
			depthbufferid = EXTFramebufferObject.glGenRenderbuffersEXT();
			EXTFramebufferObject.glBindRenderbufferEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, depthbufferid);
			EXTFramebufferObject.glRenderbufferStorageEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, EXTPackedDepthStencil.GL_DEPTH_STENCIL_EXT, xres, yres);
			EXTFramebufferObject.glFramebufferRenderbufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, EXTFramebufferObject.GL_DEPTH_ATTACHMENT_EXT, EXTFramebufferObject.GL_RENDERBUFFER_EXT, depthbufferid);
			EXTFramebufferObject.glFramebufferRenderbufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, EXTFramebufferObject.GL_STENCIL_ATTACHMENT_EXT, EXTFramebufferObject.GL_RENDERBUFFER_EXT, depthbufferid);
		}
		int framebuffer = EXTFramebufferObject.glCheckFramebufferStatusEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT);
		switch (framebuffer) {
			case EXTFramebufferObject.GL_FRAMEBUFFER_COMPLETE_EXT :
				break;
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT :
				Log.n("FrameBuffer: " + fbufferid + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT exception");
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT :
				Log.n("FrameBuffer: " + fbufferid + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT exception");
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT :
				Log.n("FrameBuffer: " + fbufferid + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT exception");
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT :
				Log.n("FrameBuffer: " + fbufferid + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT exception");
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT :
				Log.n("FrameBuffer: " + fbufferid + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT exception");
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT :
				Log.n("FrameBuffer: " + fbufferid + ", has caused a GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT exception");
			default :
				Log.n("Unexpected reply from glCheckFramebufferStatusEXT: " + framebuffer);
		}
		EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0);
	}
	public void clear() {
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
	}
	public void enable() {
		EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, fbufferid);
		if (depth || stencil)
			EXTFramebufferObject.glBindRenderbufferEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, depthbufferid);
		GLTools.reshape2D(xres, yres);
	}
	public void disable() {
		EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0);
		EXTFramebufferObject.glBindRenderbufferEXT(EXTFramebufferObject.GL_RENDERBUFFER_EXT, 0);
	}
	public void drawOnScreen(float x, float y, float w, float h) {
		GLTools.drawTexRect(x, y, w, h, texid);
	}
	public void resize(int w, int h) {
		EXTFramebufferObject.glDeleteFramebuffersEXT(fbufferid);
		if (stencil || depth)
			EXTFramebufferObject.glDeleteRenderbuffersEXT(rbufferid);
		GL11.glDeleteTextures(texid);
		GL11.glDeleteTextures(dstexid);
		this.init(new Point(w, h), stencil, depth);
	}
	public void delete() {
		GL11.glDeleteTextures(texid);
		if (this.depth || this.stencil)
			GL11.glDeleteTextures(this.dstexid);
		EXTFramebufferObject.glDeleteFramebuffersEXT(fbufferid);
	}
	public int getTexId() {
		return texid;
	}
	public int getDSTexId() {
		return dstexid;
	}
}
